/**************************************************************************************************
*													注意事项
*
*					1. Nonblock 设置超时无效  Block 设置超时有效
*					2. Select  与阻塞，非阻塞没有任何关系。
*																																					write by zhengyb
****************************************************************************************************/



#include "include/share_socket.h"


int share_outputlog(share_log_level levelog, const char *szFormat, ...)
{
#ifdef WIN32
	char buffer[512] = { 0 };
	va_list args;
	va_start(args, szFormat);
	vsprintf_s(buffer, szFormat, args);
	va_end(args);
#else

	char buffer[512] = { 0 };
	va_list args;
	va_start(args, szFormat);
	vsprintf(buffer, szFormat, args);
	printf("buffer:%s\n", buffer);
#endif
	return 0;
}
int crosetsockopt(int sockfd, int level, int optname, const void *optval, int optlen)
{
	int ret = 0;
#ifdef WIN32
	ret =  setsockopt(sockfd, level, optname, (const char *)optval, optlen);
#else
	if (SO_SNDTIMEO == optname || SO_RCVTIMEO == optname)
	{
		struct timeval time;
		int timecount = *(int*)(optval);

		time.tv_sec = timecount / 1000;

		time.tv_usec = (timecount % 1000) * 1000;
		ret =  setsockopt(sockfd, level, optname, (const char *)(&time), optlen);
	}
#endif
	return ret;
}

int RH_GetPrivateError()
{
#ifdef WIN32
	int RetError = WSAGetLastError();
#else
	int RetError = errno;
#endif
	if (RetError != 0) 
	{
		RetError = (RetError ^ -1) + 1;
	}
	else 
	{
		RetError = -11;
	}
	return RetError;
}

// *****************************************************
// function	: 以select方式TCP SOCKET 接收数据
// author 	: zhengyb		2014.9.1
// return   : Succes 0  fail -1
// parameter: Fd > 0
//            RcvBuf 不为NULL
//			  RcvLen 不为NULL ；*RcvLen 等于要发送数据长度 ；函数返回后*RcvLen 为已发送长度
//  		  Timeout 小于0 此函数为无超时阻塞模式 ；
//			  Timeout 等于0 此函数为即时返回非阻塞模式；
//			  Timeout 大于0 此函数为有超时非阻塞模式；
//			  Timeout 单位毫秒  !!!!!!!
// note		: 1.阻塞或者非阻塞socket都可以使用
//			  2.接收大数据 KB单位 例如整帧数据
//			  3.Timeout 作为Select 设定超时  并不是此函数调用超时
//******************************************************
int RH_TcpRcvNonblockFd(int Fd, char *RcvBuf, int *RcvLen, int Timeout)
{

	if (Fd < 0 || NULL == RcvBuf || RcvLen == NULL) {
		//nslog(NS_ERROR, "\n\n%s,%s,%d,<RH_TcpRcvNonblockFd IS ERROR> <FD : %d> <RcvBuf :%p> <RcvLen :%p> <Timeout :%d>\n", __FILE__, __func__, __LINE__,
		//	Fd, RcvBuf, RcvLen, Timeout);
		return RHRETFAIL;
	}

	if (*RcvLen <= 0) {
		//nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <FD : %d> <RcvBuf :%p> <RcvLen :%d> <Timeout :%d>\n",
		//	Fd, RcvBuf, *RcvLen, Timeout);
	}

	int RetSelect = -1;
	int RcvTotalLen = 0;
	int RcvBytes = 0;
	int RcvTempLen = *RcvLen;
	*RcvLen = 0;
	fd_set RcvSet;
	struct timeval Time;
	FD_ZERO(&RcvSet);
	FD_SET(Fd, &RcvSet);

	//    Time.tv_sec = Timeout;
	//    Time.tv_usec = 0;
	*RcvLen = 0;
	while (RcvTotalLen < RcvTempLen) {

		//如果timeout =0 ,表示什么 ？
		if (Timeout < 0) {
			RetSelect = select(Fd + 1, &RcvSet, NULL, NULL, NULL);
		}
		else {
			Time.tv_sec = Timeout / 1000;
			Time.tv_usec = 1000 * (Timeout % 1000);

			RetSelect = select(Fd + 1, &RcvSet, NULL, NULL, &Time);
		}
		if (RetSelect < 0) {
			//nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <select> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
			//	strerror(errno), errno, Fd);

			return RH_GetPrivateError();
		}
		else if (RetSelect == 0) {
			//	nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <Rcv timeout> <ERROR_S :%s> <ERROR_D :%d> <FD : %d> <Timeout :%d>\n",
			//      strerror(errno), errno, Fd, Timeout);
			return -1;
			//return RH_GetPrivateError();
		}
		else {
			if (FD_ISSET(Fd, &RcvSet)) {
				RcvBytes = recv(Fd, RcvBuf + RcvTotalLen, RcvTempLen - RcvTotalLen, 0);

				if (RcvBytes < 0) {
					//nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <Rcv Error> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
					//	strerror(errno), errno, Fd);
					return RH_GetPrivateError();
				}
				else if (RcvBytes == 0) {
					//			nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <Rcv Close> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
					//			      strerror(errno), errno, Fd);
					return RH_GetPrivateError();
				}
				else {
					RcvTotalLen += RcvBytes;
					*RcvLen = RcvTotalLen;
				}
			}
			else {
				//nslog(NS_ERROR, "<RH_TcpRcvNonblockFd IS ERROR> <FD_ISSET> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
				//	strerror(errno), errno, Fd);
				return RH_GetPrivateError();
			}
		}
	}

	return RHRETSUCCESS;
}
int RH_TcpSndBlockFd(int Fd, char *SndBuf, int *SndLen)
{
	if (Fd < 0 || NULL == SndBuf || SndLen == NULL) {
		//share_outputlog(NS_ERROR, "<RH_TcpSndBlockFd IS ERROR> <FD : %d> <SndBuf :%p> <SndLen :%p>\n",
		//	Fd, SndBuf, SndLen);
		return RHRETFAIL;
	}

	if (*SndLen < 0 || *SndLen == 0) {
	//	share_outputlog(NS_ERROR, "<RH_TcpSndBlockFd IS ERROR> <FD : %d> <SndBuf :%p> <SndLen :%d>\n",
		//	Fd, SndBuf, *SndLen);
	}

	int SndTotalLen = 0;
	int SndBytes	= 0;
	int SndTempLen  = *SndLen;
	*SndLen = 0;

	while(SndTotalLen < SndTempLen) {

		SndBytes = send(Fd , SndBuf + SndTotalLen, SndTempLen - SndTotalLen, 0);

		if(SndBytes < 0) {
			//share_outputlog(NS_ERROR, "<RH_TcpSndBlockFd IS ERROR> <Snd>  <ERROR_D :%d> <FD : %d>\n",
			//	 WSAGetLastError(), Fd);
			return RH_GetPrivateError();
		} else {
			SndTotalLen += SndBytes;
			*SndLen = SndTotalLen;
		}
	}

	return RHRETSUCCESS;

}
int geterror()
{
#ifdef WIN32
	return WSAGetLastError();
#else
	return errno;
#endif
}
int RH_TcpRcvBlockFd(int Fd, char *RcvBuf, int RcvLen, int *readlen)
{
	if (Fd < 0 || NULL == RcvBuf || readlen == NULL) {
		//share_outputlog(NS_ERROR, "<RH_TcpRcvBlockFd IS ERROR> <FD : %d> <RcvBuf :%p> <RcvLen :%p>\n", Fd, RcvBuf, readlen);
		return RHRETFAIL;
	}

	if (RcvLen < 0 || RcvLen == 0) {
		//share_outputlog(NS_ERROR, "<RH_TcpRcvBlockFd IS ERROR> <FD : %d> <RcvBuf :%p> <RcvLen :%d>\n", Fd, RcvBuf, RcvLen);
		return RHRETFAIL;
	}

	int RcvTotalLen = 0;
	int RcvBytes = 0;
	int timeout_cnt = 0;
	char *TempBuf = NULL;
	int RcvTempLen = 0;
	*readlen = 0;

	while (RcvTotalLen < RcvLen) {

		TempBuf = RcvBuf + RcvTotalLen;
		RcvTempLen = RcvLen - RcvTotalLen;
		RcvBytes = recv(Fd, TempBuf, RcvTempLen, 0);

		//printf( "<RH_TcpSndBlockFd IS Begin> <RcvAdress : %p> <RcvTempLen :%d><RcvLen :%d><readlen :%d><Rcv> <FD : %d>\n",
		//	 TempBuf,RcvTempLen, RcvLen, *readlen, Fd);

		if (RcvBytes < 0) {
			if (10060 == geterror()) {
				timeout_cnt++;

				if (timeout_cnt < 5) {
					//share_outputlog(NS_ERROR, "recv failed timeout_cnt=%d errno=%d-<%s>\n", timeout_cnt, WSAGetLastError(), strerror(WSAGetLastError()));
					continue;
				}
			}
			//share_outputlog(NS_ERROR, "<RH_TcpRcvBlockFd IS ERROR> <RcvAdress : %p> <RcvTempLen :%d> <RcvLen :%d><readlen :%d><Rcv> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
			//	TempBuf, RcvTempLen, RcvLen, *readlen, strerror(WSAGetLastError()), WSAGetLastError(), Fd);
			return RH_GetPrivateError();
		}
		else if (RcvBytes == 0) {
			//share_outputlog(NS_ERROR, "ret =%d ---\n", RcvBytes);
			return -1;
		}
		else {
			RcvTotalLen += RcvBytes;
			*readlen = RcvTotalLen;
		}
	}

	return RHRETSUCCESS;
}

int32_t RH_Socket(char *file, char *func, int32_t domain, int32_t type, int32_t protocol)
{
	int fd = -1;
	fd = socket(domain, type, protocol);


	return fd;
}

int32_t RH_Close(char *file, char *func, int32_t fd)
{
	if (fd < 0) {
		//share_outputlog(NS_ERROR, "[%s:%s] ,fd [%d] is invaild\n", file, func, fd);
		return 0;
	}


#ifdef WIN32
	closesocket(fd);
	fd = -1;
	return WSAGetLastError();
#else
	close(fd);
	return errno;
#endif
}
int RH_CreateTcpNoBindFd(void)
{
	int Fd = -1;
	Fd = RH_Socket((char *)__FILE__, (char *)__func__, AF_INET, SOCK_STREAM, 0);

	if (Fd < 0) {
		//share_outputlog(NS_ERROR, "<RH_CreateTcpNoBindFd IS ERROR> <FD : %d> <ERROR_S :%s> <ERROR_D: %d>\n", Fd, strerror(WSAGetLastError()), WSAGetLastError());
		return RH_GetPrivateError();
	}

	return Fd;
}
int RH_ConnetNonblockFd(int Fd, int ServPort, char *ServIp, int Timeout)
{
	if (Fd < 0 || ServPort < 1 || ServIp == NULL) {
		//share_outputlog(NS_ERROR, "<RH_ConnetNonblock IS ERROR>  <FD : %d> <ServPort: %d> <ServIp :%s>\n", Fd, ServPort, ServIp);
		return RHRETFAIL;
	}

#if 0

	if (RH_SetSndTimeoutFd(Fd, Timeout, 0) < 0) {
		printf("<RH_ConnetNonblock IS ERROR> <RH_SetSndTimeoutFd> <FD : %d> <ServPort: %d> <ServIp :%s> <Timeout :%d>\n",
			Fd, ServPort, ServIp, Timeout);
		return RHRETFAIL;
	}
	 
#endif
	unsigned long ul = 1;
	int ret = RHRETSUCCESS;
	ioctlsocket(Fd, FIONBIO, &ul);
	struct sockaddr_in serv_addr = { 0 };
	//bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(ServPort);
	//inet_aton((const char *)ServIp, (struct in_addr *)&serv_addr.sin_addr);
	serv_addr.sin_addr.s_addr = inet_addr(ServIp);
	if (connect(Fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0) {
		struct timeval Time;
		fd_set set;
		int RetSelect = 0;
		FD_ZERO(&set);
		FD_SET(Fd, &set);

		Time.tv_sec = Timeout / 1000;
		Time.tv_usec = (Timeout % 1000) * 1000;
		RetSelect = select(Fd + 1, NULL, &set, NULL, &Time);
		if (RetSelect > 0)
		{
#ifndef WIN32
			 int len = sizeof(int);
			 int err = 0;
			 getsockopt(Fd, SOL_SOCKET, SO_ERROR, (void*)(&err), (socklen_t *)&len);
			if (err == 0)
			{
				share_outputlog(NS_DEBUG, "RH_ConnetBlockFd is successful** : %u\n", errno);
				ret = RHRETSUCCESS;

			}
			else
			{
				share_outputlog(NS_ERROR, "RH_ConnetBlockFd is fail :%u\n", errno);
				ret = RH_GetPrivateError();
			}
			ul = 0;
			ioctlsocket(Fd, FIONBIO, &ul); //设置为阻塞模式
			FD_CLR(Fd, &set);
			return ret;
#endif
			ul = 0;
			ioctlsocket(Fd, FIONBIO, &ul); //设置为阻塞模式
			FD_CLR(Fd, &set);
			return RHRETSUCCESS;
		}
		//share_outputlog(NS_ERROR, "<RH_ConnetNonblock IS ERROR> <connect>  <ERROR_S :%s> <ERROR_D :%d> <FD : %d> <ServPort: %d> <ServIp :%s>\n",
		//	strerror(WSAGetLastError()), WSAGetLastError(), Fd, ServPort, ServIp);
		return RH_GetPrivateError();
	}
	return RHRETSUCCESS;
}
int RH_SetRcvTimeoutFd(int Fd, int TimeoutSec, int TimeoutUsec)
{
	if (Fd < 0 || TimeoutSec < 0 || TimeoutUsec < 0) {
		//share_outputlog(NS_ERROR, "<RH_SetRcvTimeoutFd IS ERROR>  <FD : %d>   <TimeoutSec :%d> <TimeoutUsec :%d>\n", Fd, TimeoutSec, TimeoutUsec);
		return RHRETFAIL;
	}

	int Time = 1000 * TimeoutSec ;

	if (crosetsockopt(Fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&Time, sizeof(int)) < 0) {
		//OutputDebugString(_T("RH_SetRcvTimeoutFd IS ERROR> <SO_RCVTIMEO"));
		//share_outputlog(NS_ERROR, "<RH_SetRcvTimeoutFd IS ERROR> <SO_RCVTIMEO> <FD : %d> <ERROR_S :%s> <ERROR_D :%d> <TimeoutSec :%d> <TimeoutUsec :%d>\n",
		//	Fd, strerror(WSAGetLastError()), WSAGetLastError(), TimeoutSec, TimeoutUsec);
		return RH_GetPrivateError();
	}

	return RHRETSUCCESS;
}
int RH_SetSndTimeoutFd(int Fd, int TimeoutSec, int TimeoutUsec)
{
	if (Fd < 0 || TimeoutSec < 0 || TimeoutUsec < 0) {
		//share_outputlog(NS_ERROR, "<RH_SetSndTimeoutFd IS ERROR>  <FD : %d>   <TimeoutSec :%d> <TimeoutUsec :%d>\n", Fd, TimeoutSec, TimeoutUsec);
		return RHRETFAIL;
	}

	int Time = 1000 * TimeoutSec;

	if (crosetsockopt(Fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&Time, sizeof(int)) < 0) {
		//OutputDebugString(_T("RH_SetRcvTimeoutFd IS ERROR> <SO_SNDTIMEO"));
		//share_outputlog(NS_ERROR, "<RH_SetSndTimeoutFd IS ERROR> <SO_SNDTIMEO> <FD : %d> <ERROR_S :%s> <ERROR_D :%d> <TimeoutSec :%d> <TimeoutUsec :%d>\n",
			//Fd, strerror(WSAGetLastError()), WSAGetLastError(), TimeoutSec, TimeoutUsec);
		return RH_GetPrivateError();
	}

	return RHRETSUCCESS;
}

int RH_TcpSndNonblockFd(int Fd, char *SndBuf, int *SndLen, int Timeout)
{

	if (Fd < 0 || NULL == SndBuf || SndLen == NULL) {
	//	nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <FD : %d> <SndBuf :%p> <SndLen :%p> <Timeout :%d>\n",
	//		Fd, SndBuf, SndLen, Timeout);
		return RHRETFAIL;
	}

	if (*SndLen < 0 || *SndLen == 0) {
	//	nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <FD : %d> <SndBuf :%p> <SndLen :%d> <Timeout :%d>\n",
		//	Fd, SndBuf, *SndLen, Timeout);
		return RHRETFAIL;
	}

	int RetSelect = -1;
	int SndTotalLen = 0;
	int SndBytes = 0;
	int SndTempLen = *SndLen;
	*SndLen = 0;
	fd_set SndSet;
	struct timeval Time;
	FD_ZERO(&SndSet);
	FD_SET(Fd, &SndSet);

	//    Time.tv_sec = Timeout;
	//    Time.tv_usec = 0;

	while (SndTotalLen < SndTempLen) {
		if (Timeout < 0) {
			RetSelect = select(Fd + 1, NULL, &SndSet, NULL, NULL);
		}
		else {
			Time.tv_sec = Timeout / 1000;
			Time.tv_usec = 1000 * (Timeout % 1000);

			RetSelect = select(Fd + 1, NULL, &SndSet, NULL, &Time);
		}


		if (RetSelect < 0) {
		//	nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <select> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
			//	strerror(errno), errno, Fd);
			return RH_GetPrivateError();
		}
		else if (RetSelect == 0) {
			//nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <Snd timeout> <ERROR_S :%s> <ERROR_D :%d> <FD : %d> <Timeout :%d>\n",
			///strerror(errno), errno, Fd, Timeout);
			//return RH_GetPrivateError();
			return RH_GetPrivateError();
		}
		else {
			if (FD_ISSET(Fd, &SndSet)) {
				SndBytes = send(Fd, SndBuf + SndTotalLen, SndTempLen - SndTotalLen, 0);

				if (SndBytes < 0) {
			//		nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <Snd > <sendlen :%d> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
			//			SndTempLen - SndTotalLen, strerror(errno), errno, Fd);
					return RH_GetPrivateError();
				}
				else {
					SndTotalLen += SndBytes;
					*SndLen = SndTotalLen;
				}
			}
			else {
			//	nslog(NS_ERROR, "<RH_TcpSndNonblockFd IS ERROR> <FD_ISSET> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
				//	strerror(errno), errno, Fd);
				return RH_GetPrivateError();
			}
		}
	}

	return 	RHRETSUCCESS;
}
int RH_CreateTcpBindFd(int LocalPort, char *LocalIp)
{
	int Fd = -1;

	if (LocalPort < 0 || LocalPort == 0) {
	//	nslog(NS_ERROR, "<RH_CreateTcpFd IS ERROR>  <FD : %d> <LocalPort :%d > <LocalIp :%s>\n", Fd, LocalPort, LocalIp);
		return RHRETFAIL;
	}

	struct sockaddr_in LocalAddr;

	memset(&LocalAddr,0,  sizeof(LocalAddr));

	LocalAddr.sin_family = AF_INET;
	LocalAddr.sin_port = htons(LocalPort);
	if (LocalIp)
	{
		LocalAddr.sin_addr.s_addr = inet_addr((const char *)LocalIp);
	}
	else
	{
		LocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	}



	Fd = RH_Socket(__FILE__, (char *)__func__, AF_INET, SOCK_STREAM, 0);

	if (Fd < 0) {
		//nslog(NS_ERROR, "<RH_CreateTcpBindFd IS ERROR> <SO_REUSEADDR> <FD : %d> <ERROR_S> <ERROR_D %s,%d>\n", Fd, strerror(errno), errno);
		return RH_GetPrivateError();
	}

	int opt = 1;

	if (crosetsockopt(Fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
		//nslog(NS_ERROR, "<RH_CreateTcpBindFd IS ERROR> <SO_REUSEADDR> <FD : %d> <ERROR_S> <ERROR_D %s,%d>\n", Fd, strerror(errno), errno);
		return RH_GetPrivateError();
	}


	if (bind(Fd, (struct sockaddr *) &LocalAddr, sizeof(LocalAddr)) < 0) {
		//nslog(NS_ERROR, "<RH_CreateTcpBindFd IS ERROR> <BIND> <FD : %d> <ERROR_%s> <ERROR_%d> <LocalPort :%d> <LocalIp :%s>\n",
		//	Fd, strerror(errno), errno, LocalPort, LocalIp);
		return RH_GetPrivateError();
	}

	return Fd;
}
int RH_GetConnectNonblockFd(int Fd, int Timeout, char *AcceptIp)
{
	if (Fd < 0) {
		//nslog(NS_ERROR, "<RH_GetConnectNonblockFd IS ERROR>  <FD : %d>\n", Fd);
		return RHRETFAIL;
	}

	int ClnFd = -1;
	int RetSelect = -1;
	int Len = sizeof(struct sockaddr_in);

	fd_set set;

	struct timeval Time;
	Time.tv_sec = Timeout;
	Time.tv_usec = 0;
	FD_ZERO(&set);
	FD_SET(Fd, &set);

	struct sockaddr_in ClnAddr;

	RetSelect = select(Fd + 1, &set, NULL, NULL, &Time);

	if (RetSelect > 0) {
		ClnFd = accept(Fd, (struct sockaddr *)&ClnAddr, &Len);

		if (ClnFd < 0) {
		//	nslog(NS_ERROR, "<RH_GetConnectNonblockFd IS ERROR> <listen> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n",
			//	strerror(errno), errno, Fd);
			return RH_GetPrivateError();
		}
		else {
		///	nslog(NS_INFO, "<RH_GetConnectNonblockFd IS OK> <accept> <FD : %d> <ClientAddr :%s>\n", Fd, inet_ntoa(ClnAddr.sin_addr));
			memcpy(AcceptIp, inet_ntoa(ClnAddr.sin_addr), RHADDRMAXLEN);
			return ClnFd;
		}

	}
	else if (0 == RetSelect) {
		//		nslog(NS_ERROR, "<RH_GetConnectNonblockFd IS ERROR> <Connect timeout> <ERROR_S :%s> <ERROR_D :%d> <FD : %d> <Timeout :%d>\n",
		//		      strerror(errno), errno, Fd, Timeout);
		return RH_GetPrivateError();
	}
	else {
		//nslog(NS_ERROR, "<RH_GetConnectNonblockFd IS ERROR> <select> <ERROR_S :%s> <ERROR_D :%d> <FD : %d>\n", strerror(errno), errno, Fd);
		return RH_GetPrivateError();
	}

}


